<!DOCTYPE HTML>
<html>
<!--
  Test that an unresolved respondWith promise will reset the channel when
  the service worker is terminated due to idling, and that appropriate error
  messages are logged for both the termination of the serice worker and the
  resetting of the channel.
  -->
<head>
  <title>Test for Bug 1188545</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/SpawnTask.js"></script>
  <script src="error_reporting_helpers.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a>
<p id="display"></p>
<div id="content" style="display: none">

</div>
<pre id="test">
</pre>

<script class="testbody" type="text/javascript">
// (This doesn't really need to be its own task, but it allows the actual test
// case to be self-contained.)
add_task(function setupPrefs() {
  return SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.enabled", true],
    ["dom.serviceWorkers.testing.enabled", true],
  ]});
});

add_task(function* grace_timeout_termination_with_interrupted_intercept() {
  // Setup timeouts so that the service worker will go into grace timeout after
  // a zero-length idle timeout.
  yield SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.idle_timeout", 0],
    ["dom.serviceWorkers.idle_extended_timeout", 299999]]});

  // The SW will claim us once it activates; this is async, start listening now.
  let waitForControlled = new Promise((resolve) => {
    navigator.serviceWorker.oncontrollerchange = resolve;
  });

  let registration = yield navigator.serviceWorker.register(
    "unresolved_fetch_worker.js", { scope: "./"} );
  yield waitForControlled;
  ok(navigator.serviceWorker.controller, "Controlled"); // double check!

  // We want to make sure the SW is active and processing the fetch before we
  // try and kill it.  It sends us a message when it has done so.
  let waitForFetchActive = new Promise((resolve) => {
    navigator.serviceWorker.onmessage = resolve;
  });

  // Issue a fetch which the SW will respondWith() a never resolved promise.
  // The fetch, however, will terminate when the SW is killed, so check that.
  let hangingFetch = fetch("does_not_exist.html")
    .then(() => { ok(false, "should have rejected "); },
          () => { ok(true, "hung fetch terminates when worker dies"); });

  yield waitForFetchActive;

  let expectedMessage = expect_console_message(
    // Termination error
    "ServiceWorkerGraceTimeoutTermination",
    [make_absolute_url("./")],
    // The interception failure error generated by the RespondWithHandler
    // destructor when it notices it didn't get a response before being
    // destroyed.  It logs via the intercepted channel nsIConsoleReportCollector
    // that is eventually flushed to our document and its console.
    "InterceptionFailedWithURL",
    [make_absolute_url("does_not_exist.html")]
  );

  // Zero out the grace timeout too so the worker will get terminated after two
  // zero-length timer firings.  Note that we need to do something to get the
  // SW to renew its keepalive for this to actually cause the timers to be
  // rescheduled...
  yield SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.idle_extended_timeout", 0]]});
  // ...which we do by postMessaging it.
  navigator.serviceWorker.controller.postMessage("doomity doom doom");

  // Now wait for signs that the worker was terminated by the fetch failing.
  yield hangingFetch;

  // The worker should now be dead and the error logged, wait/assert.
  yield wait_for_expected_message(expectedMessage);

  // roll back all of our test case specific preferences and otherwise cleanup
  yield SpecialPowers.popPrefEnv();
  yield SpecialPowers.popPrefEnv();
  yield registration.unregister();
});
</script>
</body>
</html>
